use std::collections::HashMap;

use common_base::serde::{self, Serialize};

use crate::{
    data::{DatasetOwner, RowsetOwner},
    ServiceContextBase,
};

use super::{metadata::MetadataBase, FctMetadata, Metadata};

#[derive(Debug, Clone)]
// #[derive( Serialize, )]
// #[serde(crate = "self::serde")]
// #[serde(rename_all = "camelCase")]
pub struct BizMetadata {
    pub(crate) base: MetadataBase,
    pub(crate) fct_map: HashMap<String, Metadata>,
    pub(crate) ctn_map: HashMap<String, Vec<FctMetadata>>,
    pub(crate) ctn_type_map: HashMap<String, Vec<String>>,
}

impl Into<Metadata> for BizMetadata {
    fn into(self) -> Metadata {
        Metadata::Biz(self)
    }
}

impl BizMetadata {
    pub const SYS_MODEL: &'static str = "SYS_MODEL";
    pub const SYS_MDL_CTN: &'static str = "SYS_MDL_CTN";
    pub const SYS_MDL_VAL: &'static str = "SYS_MDL_VAL";
    pub const FCT_COUNT: usize = 16;

    // 每种类型的业务内容最大的事实表的数据，默认为16个
    pub const BIZ_CTN_TYPE_FCT_COUNT: usize = 16;
    // 发生数类型的事实表，用于记录明细的发生数
    // 表单信息表
    pub const BIZ_CTN_TYPE_JBDS: &'static str = "JBDS";
    // 分录信息表
    pub const BIZ_CTN_TYPE_JIDS: &'static str = "JIDS";
    // 明细数据集
    pub const BIZ_CTN_TYPE_JPDS: &'static str = "JPDS";
    // 用于记录根据发生数汇总产生的余额表
    pub const BIZ_CTN_TYPE_BLDS: &'static str = "BLDS";
    // 预算控制类的余额表
    pub const BIZ_CTN_TYPE_BCDS: &'static str = "BVDS";
    pub const CTN_ID: &'static str = "CTN_ID";
    pub const CTN_FCT: &'static str = "CTN_FCT";
    pub const CTN_TYPE: &'static str = "CTN_TYPE";
    pub const PCTN_ID: &'static str = "PCTN_ID";
    pub const CTN_NAME: &'static str = "CTN_NAME";

    pub const MDL_KEY: &'static str = "MDL_KEY";
    pub const AFFIX_OBJID: &'static str = "AFFIX_OBJID";

    pub fn new(object_id: &str, rowset: RowsetOwner) -> Self {
        Self {
            base: MetadataBase::new(object_id, rowset),
            fct_map: HashMap::default(),
            ctn_map: HashMap::default(),
            ctn_type_map: HashMap::default(),
        }
    }
}

impl BizMetadata {
    pub fn metadata_base_mut(&mut self) -> &mut MetadataBase {
        &mut self.base
    }

    pub fn insert_dataset(&mut self, key: &str, dataset: DatasetOwner) {
        self.base.self_rowset.insert_dataset(key, dataset);
    }

    pub fn get_string(&self, key: &str) -> &str {
        self.base.self_rowset.get_string(key)
    }

    pub fn get_dataset(&self, dataset_key: &str) -> Option<&DatasetOwner> {
        self.base.self_rowset.get_dataset(dataset_key)
    }

    pub fn get_dataset_mut(&mut self, dataset_key: &str) -> Option<&mut DatasetOwner> {
        self.base.self_rowset.get_dataset_mut(dataset_key)
    }

    pub fn get_object_id(&self) -> &str {
        self.base.object_id.as_str()
    }

    pub fn get_extend_property(&self, unit_id: &str) -> Option<&HashMap<String, String>> {
        self.base.get_extend_property(unit_id)
    }

    pub fn get_extend_property_value(&self, key: &str) -> &str {
        self.get_extend_property_value_with_unit("", key)
    }

    pub fn get_extend_property_value_with_unit(&self, unit_id: &str, key: &str) -> &str {
        if let Some(value) = self
            .get_extend_property(unit_id)
            .and_then(|map| map.get(key))
        {
            return value.as_str();
        }

        if !unit_id.is_empty() {
            self.get_extend_property("")
                .and_then(|map| map.get(key).map(|s| s.as_str()))
                .unwrap_or("")
        } else {
            ""
        }
    }

    pub fn add_fct_metadata(&mut self, fct_metadata: Metadata) {
        self.fct_map
            .insert(fct_metadata.get_object_id().to_owned(), fct_metadata);
    }

    pub fn add_ctn_id_2_ctn_type_map(&mut self, ctn_type: &str, ctn_id: &str) {
        if !self.ctn_type_map.contains_key(ctn_type) {
            self.ctn_type_map
                .insert(ctn_type.to_owned(), Vec::default());
        }

        let ctn_list = self.ctn_type_map.get_mut(ctn_type).unwrap();
        if ctn_list.iter().find(|&x| x.eq(ctn_id)).is_none() {
            ctn_list.push(ctn_id.to_owned());
        }
    }

    pub fn add_dct_metadata(&mut self, srv_ctx: &mut ServiceContextBase, dct_metadata: Metadata) {
        srv_ctx
            .get_object_mut("DCT_MAP")
            .expect("DCT_MAP in context must not be none")
            .as_map_mut()
            .expect("DCT_MAP must initilize as a map")
            .insert(dct_metadata.get_object_id().to_owned(), dct_metadata.into());
    }

    pub fn get_dct_metadata<'a>(
        &self,
        srv_ctx: &'a ServiceContextBase,
        dct_id: &str,
    ) -> Option<&'a Metadata> {
        srv_ctx
            .get_object("DCT_MAP")
            .expect("DCT_MAP in context must not be none")
            .as_map()
            .expect("DCT_MAP must initilize as a map")
            .get(dct_id)
            .and_then(|v| v.as_object())
            .and_then(|v| v.as_any().downcast_ref::<Metadata>())
    }
}
